package com.wimcorp.magic.client.input;

import java.rmi.RemoteException;
import java.util.Set;
import java.util.concurrent.Callable;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.lang.StringUtils;
import org.fusesource.jansi.Ansi.Color;

import com.wimcorp.magic.client.CommandLineInterface;
import com.wimcorp.magic.exception.SystemException;
import com.wimcorp.magic.input.Target;
import com.wimcorp.magic.input.Type;
import com.wimcorp.magic.userinterface.GameInterface;

public class CardSelector extends Selector implements Callable<Long> {

	private Target target;
	private Set<String> types;
	private Set<String> locations;
	private Set<String> behaviors;
	private String message;

	public CardSelector(CommandLineInterface commandLineInterface, Target target, Set<String> types,
			Set<String> locations, Set<String> behaviors) {
		super(commandLineInterface);
		this.target = target;
		this.types = types;
		this.locations = locations;
		this.behaviors = behaviors;
		if (Target.OPPONENT.toString().equals(target)) {
			message = "Select opponent " + getArrayString(types) + " : ";
		} else {
			message = "Select " + getArrayString(types) + " from " + getArrayString(locations) + " : ";
		}
	}
	
	public CardSelector(CommandLineInterface commandLineInterface,String message, Target target, Set<String> types,
			Set<String> locations, Set<String> behaviors) {
		super(commandLineInterface);
		this.target = target;
		this.types = types;
		this.locations = locations;
		this.behaviors = behaviors;
		this.message = message;
	}

	@Override
	public Long call() {
		return select(target, types, locations, behaviors);
	}

	private long select(Target target, Set<String> types, Set<String> locations, Set<String> behaviors) {
		Options options = getSelectOptions(types, locations, behaviors);
		
		ConsoleInput selectConsole = new ConsoleInput();
		ConsoleInput mainConsole = getCommandLineInterface().getMainConsole();
		CommandLineParser parser = new DefaultParser();
		Long result = null;
		setRun(true);
		while (isRun()) {
			mainConsole.cancel();
			getCommandLineInterface().printInstruction(message);
			try {
				CommandLine line = parser.parse(options, selectConsole.readLine().split(" "));
				for (Option option : line.getOptions()) {
					if ("s".equals(option.getOpt())) {
						result = execute(option);
					} else {
						result = processCommands(options, line.getOptions());
					}
				}
			} catch (Exception e) {
				getCommandLineInterface().print(e.getMessage(), Color.RED);
			}
		}
		synchronized (mainConsole.getLock()) {
			mainConsole.getLock().notifyAll();
		}
		return result;
	}

	private Long execute(Option option) throws RemoteException, SystemException {
		Long result = null;
		String input = option.getValue();
		String playerName = getCommandLineInterface().getPlayerName();
		GameInterface game = getCommandLineInterface().getGame();
		String opponentName = game.getOpponent(playerName);
		if (StringUtils.isNumeric(input)) {
			long identifier = new Long(input);
			switch (target) {
			case SELF:
				result = game.validateInput(playerName, types, locations, behaviors, identifier);
				break;
			case OPPONENT:
				result = game.validateInput(opponentName, types, locations, behaviors, identifier);
				break;
			case ANY:
				result = game.validateInput(opponentName, types, locations, behaviors, identifier);
				if (result == -1) {
					result = game.validateInput(playerName, types, locations, behaviors, identifier);
				}
				break;
			}
			if (result == -1) {
				throw new SystemException("No " + getArrayString(types) + " in " + getArrayString(locations) + " with "
						+ getArrayString(behaviors) + " id : " + input + ". ");
			}
			setRun(false);
		} else if (types.contains(Type.PLAYER.toString())) {
			if (playerName.equals(input)
					&& (Target.SELF.toString().equals(target) || Target.ANY.toString().equals(target))) {
				// Return -100 for self
				setRun(false);
				result = -100l;
			} else if (opponentName.equals(input)
					&& (Target.OPPONENT.toString().equals(target) || Target.ANY.toString().equals(target))) {
				// Return -200 for opponent
				setRun(false);
				result = -200l;
			} else {
				throw new SystemException("No player with name " + input + " found. ");
			}
		}
		return result;

	}

	public Options getSelectOptions(Set<String> types, Set<String> locations, Set<String> behaviors) {
		Options options = new Options();
		options.addOption(getHelpOption());
		options.addOption(getDoneOption());
		options.addOption(getSelectOption(types, locations, behaviors));
		options.addOption(getPlayerListOption());
		options.addOption(getViewBattleFieldOption());
		options.addOption(getViewGraveyardOption());
		options.addOption(getViewHandOption());
		options.addOption(getViewLifeOption());
		return options;
	}

}
